/**
*
*/
package jframe.pay.http.handler;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.serializer.SerializeFilter;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
import jframe.core.msg.Msg;
import jframe.core.plugin.annotation.InjectPlugin;
import jframe.core.plugin.annotation.Injector;
import jframe.pay.domain.Fields;
import jframe.pay.domain.http.RspCode;
import jframe.pay.domain.util.HttpUtil;
import jframe.pay.domain.util.JsonUtil;
import jframe.pay.http.HttpConstants;
import jframe.pay.http.PayHttpPlugin;
/**
* TODO dispose
*
* @author dzh
* @date Jul 25, 2014 11:23:15 AM
* @since 1.0
*/
@Injector
public abstract class AbstractHandler extends SimpleChannelInboundHandler<HttpObject> {
static Logger LOG = LoggerFactory.getLogger(AbstractHandler.class);
static Logger LOG_REQ = LoggerFactory.getLogger("jframe.pay.http.handler.req");
@InjectPlugin
protected static PayHttpPlugin Plugin;
protected boolean keepAlive = false;
protected boolean gzip = false;
protected Map<String, Object> rspMap;
protected ChannelHandlerContext ctx;
protected AbstractHandler() {
this.rspMap = initRespMap();
}
private Map<String, List<String>> _params;
protected StringBuilder data;
// private ByteBufOutputStream data;
protected HttpRequest req;
protected void putRspMap(String key, Object value) {
rspMap.put(key, value);
}
/**
*
*/
public boolean acceptInboundMessage(Object msg) throws Exception {
if (msg instanceof HttpRequest) {
// TODO
}
return super.acceptInboundMessage(msg);
}
/*
* (non-Javadoc)
*
* @see io.netty.channel.SimpleChannelInboundHandler#channelRead0(io.netty.
* channel .ChannelHandlerContext, java.lang.Object)
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (this.ctx == null)
this.ctx = ctx;
if (msg instanceof HttpRequest) {
readHttpRequest((HttpRequest) msg);
if (LOG_REQ.isDebugEnabled()) {
LOG_REQ.debug("Start reqUrl->{},ip->{},date->{}", getReqUrl(), getRemoteIp(), new Date().getTime());
}
} else if (msg instanceof HttpContent) {
readHttpContent((HttpContent) msg);
}
}
public String getRemoteIp() {
// nginx时设置客户端真实ip
String remoteIp = getHttpRequest().headers().get("X-Real-Ip");
if (Objects.isNull(remoteIp)) {
remoteIp = ((InetSocketAddress) getChannelHandlerContext().channel().remoteAddress()).getAddress()
.getHostAddress();
}
return remoteIp;
}
protected void readHttpRequest(HttpRequest msg) throws Exception {
HttpRequest req = (HttpRequest) msg;
AbstractHandler.this.req = req;
if (!req.getMethod().equals(HttpMethod.POST) || !isValidHeaders(req.headers())) {
finish(ctx);
return;
}
keepAlive = HttpHeaders.isKeepAlive(req);
String encoding = req.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
if (encoding != null && encoding.indexOf("gzip") != -1) {
gzip = true;
}
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.getUri());
_params = queryStringDecoder.parameters();
if (LOG.isDebugEnabled()) {
LOG.debug("Receive request -> {}", req.getUri());
}
}
// TODO buf
protected void readHttpContent(HttpContent msg) throws Exception {
StringBuilder buf = data == null ? data = new StringBuilder() : data;
buf.append(msg.content().toString(getReqCharset()));
if (msg instanceof LastHttpContent) {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Dispatch req buf->{} req->{}", buf.toString(),
parseHttpReq(URLDecoder.decode(buf.toString(), HttpConstants.UTF8)));
}
String content = parseReqContent(buf.toString());
if (isValidData(content)) {
service(parseHttpReq(content), rspMap);
} else {
RspCode.setRspCode(rspMap, RspCode.FAIL_SIGN_ERROR);
}
} finally {
finish(ctx);
}
if (LOG_REQ.isDebugEnabled()) {
LOG_REQ.debug("Finish reqUrl->{},ip->{},date->{}", getReqUrl(), getRemoteIp(),
System.currentTimeMillis());
}
}
}
protected String parseReqContent(String content) throws UnsupportedEncodingException {
return URLDecoder.decode(content, HttpConstants.UTF8);
}
protected Charset getReqCharset() {
return CharsetUtil.UTF_8;
}
protected boolean isValidData(String data) {
return Objects.nonNull(data);
}
/**
* 请求接口版本 TODO
*
* @return
*/
public String getPayVern() {
return req.headers().get("pay-vern");
}
/**
* 任何一个url的最后一段是请求码
*
* @return
*/
protected String getReqOp() {
String[] path = req.getUri().substring(1).split("\\/");
String reqOp = path.length == 0 ? "" : path[path.length - 1];
int loc = reqOp.indexOf('?');
if (loc != -1) {
return reqOp.substring(0, loc);
}
return reqOp;
}
protected String getReqUrl() {
String reqUrl = req.getUri();
int loc = reqUrl.indexOf('?');
if (loc != -1) {
return reqUrl.substring(0, loc);
}
return reqUrl;
}
protected String[] getReqPath() {
return req.getUri().substring(1).split("\\/");
}
protected void finish(ChannelHandlerContext ctx) throws Exception {
Map<String, Object> resp = null;
try {
resp = filterRspMap(rspMap);
} finally {
writeResponse(ctx, resp);
ctx.pipeline().remove(this);
if (data != null) {
// data.close();
data = null;
}
}
}
public HttpRequest getHttpRequest() {
return req;
}
public HttpHeaders getHttpHeaders() {
if (req != null)
return req.headers();
return null;
}
/**
* @param headers
* @return
*/
public boolean isValidHeaders(HttpHeaders headers) {
return true;
}
protected Map<String, String> parseHttpReq(String content) throws Exception {
// if (content.indexOf('=') != -1) {
// return parseHttpParas(content);
// }
// return JsonUtil.decode(content);
Map<String, String> req = HttpUtil.parseHttpParas(content.trim());
req.put("reqUrl", getReqUrl());
return req;
}
/**
* @param respMap2
* @return
*/
protected Map<String, Object> filterRspMap(Map<String, Object> rsp) {
return rsp;
}
public List<String> getReqPara(String key) {
List<String> l = _params.get(key);
if (l == null)
return Collections.emptyList();
return l;
}
protected Map<String, List<String>> getPara() {
return _params;
}
/**
*
* @param key
* @param defVal
* 默认值
* @return
*/
public String getReqPara(String key, String defVal) {
List<String> l = _params.get(key);
if (l != null && l.size() > 0)
return l.get(0);
return defVal;
}
/**
* @param req
* @param resp
*/
public abstract void service(Map<String, String> req, Map<String, Object> rsp) throws PayException;
protected Map<String, Object> initRespMap() {
return new HashMap<String, Object>();
}
protected void writeResponse(ChannelHandlerContext ctx, Map<String, Object> rsp) {
if (LOG.isDebugEnabled()) {
LOG.debug("response {}", rsp);
}
FullHttpResponse response = createHttpResponse(rsp);
ChannelFuture future = ctx.writeAndFlush(response);
if (!keepAlive) {
LOG.info("close channel {}", this.getReqUrl());
future.addListener(ChannelFutureListener.CLOSE);
}
// future.channel().close();
// TODO
}
protected FullHttpResponse createHttpResponse(Map<String, Object> rsp) {
FullHttpResponse response = null;
if (rsp == null || rsp.isEmpty()) {
response = new DefaultFullHttpResponse(HTTP_1_1, OK);
} else {
if (rsp.containsKey(Fields.F_rspOut)) {
response = new DefaultFullHttpResponse(HTTP_1_1, OK,
Unpooled.copiedBuffer(rsp.get(Fields.F_rspOut).toString(), CharsetUtil.UTF_8));
} else {
Object filter = rsp.remove(Fields.F_filter);
if (filter != null && filter instanceof SerializeFilter) {
response = new DefaultFullHttpResponse(HTTP_1_1, OK,
Unpooled.copiedBuffer(JsonUtil.encode(rsp, (SerializeFilter) filter), CharsetUtil.UTF_8));
} else {
response = new DefaultFullHttpResponse(HTTP_1_1, OK,
Unpooled.copiedBuffer(JsonUtil.encode(rsp), CharsetUtil.UTF_8));
}
}
}
if (gzip) {
// response.headers().set(CONTENT_ENCODING, "gzip");
}
response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
if (keepAlive) {
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
}
return response;
}
public void sendMsg(Msg<?> msg) {
Plugin.send(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
LOG.error(cause.getMessage(), cause);
finish(ctx);
}
public ChannelHandlerContext getChannelHandlerContext() {
return ctx;
}
}